Arreglo
unidimensional con etiquetas en los ejes (incluidas series de tiempo). Los parámetros de una Serie
son: data
(matriz, diccionario o escalar), index
(arreglo de índices), dtype
(numpy.dtype
o None
) y copy
(booleano o por defecto False
)
Importamos la biblioteca Pandas
In [1]:
import pandas as pd
pd.Series?
Podemos convertir una lista
en una serie
y pandas asigna de manera inmediata una lista de índices que empieza en 0.
In [2]:
animales = ['Tigre', 'Oso', 'Camello']
pd.Series(animales)
Out[2]:
In [3]:
numeros = [1, 2, 3]
pd.Series(numeros)
Out[3]:
In [4]:
animales = ['Tigre', 'Oso', None]
pd.Series(animales)
Out[4]:
Es importante saber como NumPy
y Pandas
manejan los datos faltantes. En Python tenemos el tipo None
para indicar un dato faltante. Si tenemos una lista de números, Pandas
automáticamente convierte este valor None
en un valor designado como NaN
, que significa Not a Number
.
In [5]:
numeros = [1, 2, None]
pd.Series(numeros)
Out[5]:
Importamos la biblioteca NumPy
. También es importante saber que NaN
no es None
. Cuando hacemos un test para saber si NaN
es NaN
tambien obtendremos False
.
In [6]:
import numpy as np
np.nan == None
Out[6]:
In [7]:
np.nan == np.nan
Out[7]:
Se necesita la función especial isnan
de NumPy
para chequear la presencia de un no número
en nuestros datos.
In [8]:
print(np.isnan(np.nan))
print(None is None)
print(np.nan is np.nan)
¿Cómo creamos una serie
en Pandas
? Podemos utilizar una estructura de datos diccionario
con sus claves
y convertirlo en una serie
, donde los índices de la serie
son las claves del diccionario.
In [9]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes)
s
Out[9]:
Luego, podemos chequear la lista de índices con el atributo .index
In [10]:
s.index
Out[10]:
En este otro ejemplo, pasamos directamente una lista con su conjunto de índices para crear la Serie
.
In [11]:
s = pd.Series(['Tigre', 'Oso', 'Camello'], index=['India', 'America', 'Africa'])
s
Out[11]:
Aquí tenemos un ejemplo de un elemento nuevo en la lista de índices que no tiene un valor asignado, no existe un país asociado al índice Natación
y Pandas
representa este valor faltante con NaN
.
In [12]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes, index=['Capoeira', 'Sumo', 'Pelota Vasca', 'Natación'])
s
Out[12]:
In [13]:
deportes = {'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'}
s = pd.Series(deportes)
s
Out[13]:
Podemos hacer búsquedas en las series
por posición de índices o por etiqueta de índices. Si queremos hacer búsqueda por ubicación numérica (empezando desde 0) utilizamos el atributo iloc
. Si por otra parte, hacemos búqueda por etiqueta de índice entonces usamos el atributo loc
.
In [14]:
s.iloc[4]
Out[14]:
In [15]:
s.loc['Pelota Vasca']
Out[15]:
Pandas
trata de que el código sea más legible. Si le pasamos por parámetro un valor numérico a la Serie
esta se comportará como si la búsqueda se hace con el atributo iloc
, si en cambio le pasamos un objeto, hará la búsqueda por etiqueta como con el atributo loc
.
In [16]:
s[4]
Out[16]:
In [17]:
s['Pelota Vasca']
Out[17]:
¿Qué pasa cuando tenemos una lista de índices que son enteros?
In [18]:
deportes = {99: 'Brasil',
100: 'Chile',
101: 'País Vasco',
102: 'Cuba',
103: 'Gales',
104: 'Escocia',
105: 'España',
106: 'Japón'}
s = pd.Series(deportes)
s
Out[18]:
Cuando tenemos un caso como este es más seguro utilizar los atributos iloc
o loc
según sea el caso.
In [19]:
s[0] #Esta instrucción no llamará s.iloc[0] como esperaríamos y va a generar un error
In [ ]:
s.iloc[0]
In [ ]:
s.loc[99]
Ya que sabemos hacer búsquedas en las Series
, ahora vamos a trabajar con los datos (encontrar valores, resumir los datos o transformarlos).
In [20]:
s = pd.Series([105.00, 223.00, 5, 102.00, 27, -126])
s
Out[20]:
Una forma de trabajar es iterar sobre un conjunto de datos e invocar una operación de interés
In [21]:
total = 0
for elemento in s:
total+=elemento
print(total)
Con NumPy
podemos tener acceso a las funciones universales binarias o unarias (vectorizadas, cálculos más rápidos). En este ejemplo, np.sum
hará la suma de todos los elementos en la serie
.
In [22]:
import numpy as np
total = np.sum(s)
print(total)
También podemos generar una serie
grande de números aleatorios y con el método .head()
podemos desplegar un encabezado con los 5 primeros elementos de la serie
y con len
chequear el tamaño de la misma.
In [23]:
s = pd.Series(np.random.randint(0,1000,10000))
print(s.head())
print(len(s))
Los cuadernos de Jupyter
tienen funciones mágicas que pueden ser útiles. Una de ellas es %%timeit
que nos servirá para ver cuál de los dos métodos para sumar elementos de una serie
es más rápido.
Basta con tipear el símbolo %
y la tecla Tab
para obtener una lista de las funciones mágicas de Jupyter
.
In [24]:
%%timeit -n 100
sumar = 0
for elemento in s:
sumar+=elemento
In [25]:
%%timeit -n 100
sumar = np.sum(s)
NumPy
y Pandas
tienen el broadcasting
, se puede aplicar una operación a cada valor de la serie
y modificarla.
In [26]:
s+=2 #Suma 2 a cada elemento de la serie usando broadcasting
s.head()
Out[26]:
Una manera poco eficiente de hacer esto es iterar sobre cada elemento de la serie
para hacer la suma. El método .iteritems()
devuelve un iterador sobre los pares (key, value)
(clave, valor) de un diccionario, en este caso de nuestra serie
s.
In [27]:
for etiqueta, valor in s.iteritems():
s.set_value(etiqueta, valor+2)
s.head()
Out[27]:
In [ ]:
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
for etiqueta, valor in s.iteritems():
s.loc[etiqueta]= valor+2
In [ ]:
%%timeit -n 10
s = pd.Series(np.random.randint(0,1000,10000))
s+=2
Podemos agregar elementos a una serie
de la siguiente forma:
In [2]:
import pandas as pd
s = pd.Series([1, 2, 3])
s.loc['Animal'] = 'Oso'
s
Out[2]:
Este es un ejemplo de una serie
donde los valores del conjunto de índices no son únicos. Esto hace que las tablas de datos funcionen diferente y es por ello que agregar nuevos elementos debe hacerse con el método append
, que en primera instancia, no modificará la serie
sino que devuelve una nueva serie
con los elementos que se agregaron.
In [4]:
deportes_originales = pd.Series({'Capoeira': 'Brasil',
'Rayuela': 'Chile',
'Pelota Vasca': 'País Vasco',
'Béisbol': 'Cuba',
'Rugby': 'Gales',
'Golf': 'Escocia',
'Corrida de Toros': 'España',
'Sumo': 'Japón'})
paises_que_aman_el_beisbol = pd.Series(['Venezuela',
'USA',
'Cuba',
'Puerto Rico',
'Dominicana'],
index=['Béisbol',
'Béisbol',
'Béisbol',
'Béisbol',
'Béisbol'])
todos_los_paises = deportes_originales.append(paises_que_aman_el_beisbol)
In [5]:
deportes_originales
Out[5]:
In [6]:
paises_que_aman_el_beisbol
Out[6]:
In [7]:
todos_los_paises
Out[7]:
In [8]:
todos_los_paises.loc['Béisbol']
Out[8]:
El DataFrame
o Tabla de Datos es el corazón de la biblioteca Pandas
. Es el objeto primario para el análisis de datos. Es una especie de arreglo bidimensional con etiquetas en los ejes. En este ejemplo, crearemos tres diccionarios que serán luego las filas de nuestro DataFrame
.
In [9]:
import pandas as pd
compra_1 = pd.Series({'Nombre': 'Adelis',
'Artículo comprado': 'Libro',
'Costo': 1200})
compra_2 = pd.Series({'Nombre': 'Miguel',
'Artículo comprado': 'Raspberry pi 3',
'Costo': 15000})
compra_3 = pd.Series({'Nombre': 'Jaime',
'Artículo comprado': 'Balón',
'Costo': 5000})
df = pd.DataFrame([compra_1, compra_2, compra_3], index=['Tienda 1', 'Tienda 1', 'Tienda 2'])
df.head()
Out[9]:
En un DataFrame
también se puede extraer información usando los atributos loc
y iloc
.
In [10]:
df.loc['Tienda 2']
Out[10]:
También podemos chequear el tipo de dato usando la función type
de Python.
In [11]:
type(df.loc['Tienda 2'])
Out[11]:
En los DataFrame
también se pueden tener listas de índices no únicos. En el ejemplo, hay dos índices con el mismo nombre Tienda 1
.
In [12]:
df.loc['Tienda 1']
Out[12]:
También podemos seleccionar columnas agregando un parámetro extra al atributo loc
.
In [13]:
df.loc['Tienda 1', 'Costo']
Out[13]:
Usar el atributo .T
para obtener la transpuesta del DataFrame
o Tabla de Datos.
In [14]:
df.T
Out[14]:
Usando .T.loc[]
se puede seleccionar una columna usando como parámetro la etiqueta de su nombre.
In [15]:
df.T.loc['Costo']
Out[15]:
In [17]:
df['Costo']
Out[17]:
In [18]:
df.loc['Tienda 1']['Costo']
Out[18]:
loc
también tiene soporte para rebanar o seleccionar del DataFrame
con la notación []
In [19]:
df.loc[:,['Nombre', 'Costo']]
Out[19]:
También podemos eliminar datos del DataFrame
con la función drop()
. Esta función toma un solo parámetro que es el índice del conjunto de datos que deseamos eliminar.
In [20]:
df.drop('Tienda 1')
Out[20]:
Podemos ver que nuestro DataFrame
original sigue intacto. Solo hicimos una extracción de información.
In [21]:
df
Out[21]:
También podemos hacer una copia del DataFrame
con la función copy()
para guardar la extracción de información.
In [24]:
copiar_df = df.copy()
copiar_df = copiar_df.drop('Tienda 1')
copiar_df
Out[24]:
In [26]:
copiar_df.drop?
Podemos eliminar una columna de manera sencilla, usando simplemente la palabra clave del
y el índice o nombre de la comuna.
In [27]:
del copiar_df['Costo']
copiar_df
Out[27]:
Finalmente, es muy sencillo agregar una columna al DataFrame
.
In [28]:
df['Ubicación'] = ['Venezuela', 'Chile', 'Argentina']
df
Out[28]:
Usemos !cat
para leer un archivo de formato CSV
. Nota: !cat
funciona para Linux y Mac pero puede no funcionar para Windows :(
In [2]:
!cat olympics.csv
Pero ... no hay que preocuparse mucho por eso! Podemos leer este archivo en formato CSV
en un DataFrame
usando la función read_csv
.
In [5]:
import pandas as pd
df = pd.read_csv('olympics.csv')
df.head()
Out[5]:
Aquí podemos ignorar la primera fila del DataFrame
para dejar más limpia la tabla de información no relevante.
In [6]:
df = pd.read_csv('olympics.csv', index_col = 0, skiprows=1)
df.head()
Out[6]:
El atributo .columns
nos permite ver el nombre de las comlumnas del DataFrame
y el atributo .rename
modificar el nombre.
In [7]:
df.columns
Out[7]:
In [8]:
for col in df.columns:
if col[:2]=='01':
df.rename(columns={col:'Gold' + col[4:]}, inplace=True)
if col[:2]=='02':
df.rename(columns={col:'Silver' + col[4:]}, inplace=True)
if col[:2]=='03':
df.rename(columns={col:'Bronze' + col[4:]}, inplace=True)
if col[:1]=='№':
df.rename(columns={col:'#' + col[1:]}, inplace=True)
df.head()
Out[8]:
Podemos buscar en el DataFrame
con una máscara Booleana qué países tienen (True
) o no (False
) una medalla de oro.
In [9]:
df['Gold'] > 0
Out[9]:
La función .where()
toma una máscara booleana como condición en el argumento, la aplica al DataFrame
, y devuelve un DataFrame
de la misma forma. En nuestro ejemplo, reemplaza con NaN
los casos False
y con su valor original, los casos True
.
In [10]:
only_gold = df.where(df['Gold'] > 0)
only_gold.head()
Out[10]:
Podemos contar cuántas países hay medallas de oro hay en total con count()
In [11]:
only_gold['Gold'].count()
Out[11]:
Si contamos sobre los datos originales, veremos que hay 147 países. Cuenta los países para los cuales la máscara Booleana dio False
>.<
In [12]:
df['Gold'].count()
Out[12]:
Podemos establecer otro tipo de condiciones para hacer búsquedas más complejas. Por ejemplo, buscar la cantidad de países que han ganado medalla de oro alguna vez.
In [15]:
len(df[(df['Gold'] > 0) | (df['Gold.1'] > 0)])
Out[15]:
Buscar qué países han ganado sólo medallas de oro en Invierno y nunca en Verano.
In [16]:
df[(df['Gold.1'] > 0) & (df['Gold'] == 0)]
Out[16]:
In [ ]: